/****************************************************************************************************************************************** 
* 文件名称:	system_SWM181.c
* 功能说明:	SWM181单片机的时钟设置
* 技术支持:	http://www.synwit.com.cn/e/tool/gbook/?bid=1
* 注意事项:
* 版本日期: V1.0.0		2016年1月30日
* 升级记录: 
*
*
*******************************************************************************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS WITH CODING INFORMATION 
* REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. AS A RESULT, SYNWIT SHALL NOT BE HELD LIABLE 
* FOR ANY DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT 
* OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION CONTAINED HEREIN IN CONN-
* -ECTION WITH THEIR PRODUCTS.
*
* COPYRIGHT 2012 Synwit Technology
*******************************************************************************************************************************************/
#include <stdint.h>
#include "SWM181.h"

/******************************************************************************************************************************************
 * 系统时钟设定
 *****************************************************************************************************************************************/
#define SYS_CLK_24MHz 0 //0 内部高频24MHz RC振荡器
#define SYS_CLK_6MHz 1  //1 内部高频 6MHz RC振荡器
#define SYS_CLK_48MHz 2 //2 内部高频48MHz RC振荡器
#define SYS_CLK_12MHz 3 //3 内部高频12MHz RC振荡器
#define SYS_CLK_32KHz 4 //4 内部低频32KHz RC振荡器
#define SYS_CLK_XTAL 5  //5 外部XTAL晶体振荡器（2-30MHz）

#define SYS_CLK SYS_CLK_24MHz

#define __HSI (24000000UL) //高速内部时钟
#define __LSI (32000UL)	//低速内部时钟
#define __HSE (24000000UL) //高速外部时钟

uint32_t SystemCoreClock = __HSI;		  //System Clock Frequency (Core Clock)
uint32_t CyclesPerUs = (__HSI / 1000000); //Cycles per micro second

/****************************************************************************************************************************************** 
* 函数名称: 
* 功能说明: This function is used to update the variable SystemCoreClock and must be called whenever the core clock is changed
* 输    入: 
* 输    出: 
* 注意事项: 
******************************************************************************************************************************************/
void SystemCoreClockUpdate(void)
{
	if (SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //SYS_CLK  <= HFCK
	{
		if (SYS->CLKSEL & SYS_CLKSEL_HFCK_Msk) //HFCK <= HRC/4
		{
			if (SYS->HRCCR & SYS_HRCCR_DBL_Msk) //HRC = 48MHz
			{
				SystemCoreClock = __HSI * 2 / 4;
			}
			else //HRC = 24MHz
			{
				SystemCoreClock = __HSI / 4;
			}
		}
		else //HFCK <= HRC
		{
			if (SYS->HRCCR & SYS_HRCCR_DBL_Msk) //HRC = 48MHz
			{
				SystemCoreClock = __HSI * 2;
			}
			else //HRC = 24MHz
			{
				SystemCoreClock = __HSI;
			}
		}
	}
	else //SYS_CLK  <= LFCK
	{
		if (SYS->CLKSEL & SYS_CLKSEL_LFCK_Msk) //LFCK <= XTAL
		{
			SystemCoreClock = __HSE;
		}
		else //LFCK <= LRC
		{
			SystemCoreClock = __LSI;
		}
	}
}

static void switchToRC24MHz(void);
static void switchToRC6MHz(void);
static void switchToRC48MHz(void);
static void switchToRC12MHz(void);
static void switchToRC32KHz(void);
static void switchToXTAL(void);
/****************************************************************************************************************************************** 
* 函数名称: 
* 功能说明: The necessary initializaiton of systerm
* 输    入: 
* 输    出: 
* 注意事项: 
******************************************************************************************************************************************/
void SystemInit(void)
{
	uint32_t i;

	SYS->CLKEN |= (1 << SYS_CLKEN_OSC_Pos);

	switch (SYS_CLK)
	{
	case SYS_CLK_24MHz:						  //0 内部高频24MHz RC振荡器
		if (SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC，修改高频RC时钟频率时需要先切到一个稳定时钟源
		{
			switchToRC32KHz();
		}
		switchToRC24MHz();
		break;

	case SYS_CLK_6MHz:						  //1 内部高频 6MHz RC振荡器
		if (SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC，修改高频RC时钟频率时需要先切到一个稳定时钟源
		{
			switchToRC32KHz();
		}
		switchToRC6MHz();
		break;

	case SYS_CLK_48MHz:						  //2 内部高频48MHz RC振荡器
		if (SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC，修改高频RC时钟频率时需要先切到一个稳定时钟源
		{
			switchToRC32KHz();
		}
		switchToRC48MHz();
		break;

	case SYS_CLK_12MHz:						  //3 内部高频12MHz RC振荡器
		if (SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) //当前时钟是高频RC，修改高频RC时钟频率时需要先切到一个稳定时钟源
		{
			switchToRC32KHz();
		}
		switchToRC12MHz();
		break;

	case SYS_CLK_32KHz: //4 内部低频32KHz RC振荡器
		if ((SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) == 0)
		{
			switchToRC24MHz();
		}
		switchToRC32KHz();
		break;

	case SYS_CLK_XTAL: //5 外部XTAL晶体振荡器（2-30MHz）
		if ((SYS->CLKSEL & SYS_CLKSEL_SYS_Msk) == 0)
		{
			switchToRC24MHz();
		}
		switchToXTAL();
		break;
	}

	for (i = 0; i < 10000; i++)
		; //等待时钟稳定。。。

	SystemCoreClockUpdate();
}

static void switchToRC24MHz(void)
{
	SYS->HRCCR = (1 << SYS_HRCCR_EN_Pos) |
				 (0 << SYS_HRCCR_DBL_Pos); //HRC = 24MHz

	SYS->CLKSEL &= ~SYS_CLKSEL_HFCK_Msk;	  //HFCK  <=  HRC
	SYS->CLKSEL |= (1 << SYS_CLKSEL_SYS_Pos); //SYS_CLK  <= HFCK
}

static void switchToRC6MHz(void)
{
	SYS->HRCCR = (1 << SYS_HRCCR_EN_Pos) |
				 (0 << SYS_HRCCR_DBL_Pos); //HRC = 24MHz

	SYS->CLKSEL |= (1 << SYS_CLKSEL_HFCK_Pos); //HFCK  <=  HRC/4
	SYS->CLKSEL |= (1 << SYS_CLKSEL_SYS_Pos);  //SYS_CLK  <= HFCK
}

static void switchToRC48MHz(void)
{
	SYS->HRCCR = (1 << SYS_HRCCR_EN_Pos) |
				 (1 << SYS_HRCCR_DBL_Pos); //HRC = 48MHz

	SYS->CLKSEL &= ~SYS_CLKSEL_HFCK_Msk;	  //HFCK  <=  HRC
	SYS->CLKSEL |= (1 << SYS_CLKSEL_SYS_Pos); //SYS_CLK  <= HFCK
}

static void switchToRC12MHz(void)
{
	SYS->HRCCR = (1 << SYS_HRCCR_EN_Pos) |
				 (1 << SYS_HRCCR_DBL_Pos); //HRC = 48MHz

	SYS->CLKSEL |= (1 << SYS_CLKSEL_HFCK_Pos); //HFCK  <=  HRC/4
	SYS->CLKSEL |= (1 << SYS_CLKSEL_SYS_Pos);  //SYS_CLK  <= HFCK
}

static void switchToRC32KHz(void)
{
	SYS->LRCCR = (1 << SYS_LRCCR_EN_Pos);

	SYS->CLKSEL &= ~(1 << SYS_CLKSEL_LFCK_Pos); //LFCK  <=  LRC
	SYS->CLKSEL &= ~(1 << SYS_CLKSEL_SYS_Pos);  //SYS_CLK  <= LFCK
}

static void switchToXTAL(void)
{
	uint32_t i;

	PORT_Init(PORTC, PIN1, PORTC_PIN1_XTAL_IN, 0);
	PORT_Init(PORTC, PIN0, PORTC_PIN0_XTAL_OUT, 0);

	SYS->XTALCR = (1 << SYS_XTALCR_EN_Pos);

	for (i = 0; i < 22118; i++)
		;

	SYS->CLKSEL |= (1 << SYS_CLKSEL_LFCK_Pos); //LFCK  <=  XTAL
	SYS->CLKSEL &= ~(1 << SYS_CLKSEL_SYS_Pos); //SYS_CLK  <= LFCK
}
